commonlibsse_ng\re\c\Calendar/
month.rs

1/// 0-based Month representation.
2///
3/// Internally stores the month as `f32` in the range `0.0..=11.0`, corresponding to:
4/// - `0.0` -> January (Morning Star)
5/// - `1.0` -> February (Sun's Dawn)
6/// - `11.0` -> December (Evening Star)
7///
8/// # Example
9/// ```
10/// use commonlibsse_ng::re::Calendar::MonthIndex;
11///
12/// let month = MonthIndex::new(0.0);
13/// assert_eq!(month.to_clamp_month(), Some(1)); // 1-based month
14///
15/// let month = MonthIndex::new(11.0);
16/// assert_eq!(month.to_clamp_month(), Some(12)); // Evening Star
17///
18/// let month = MonthIndex::new(12.0);
19/// assert_eq!(month.to_clamp_month(), None); // Out of range
20/// ```
21#[derive(Debug, Default, Clone, Copy, PartialEq)]
22#[repr(transparent)]
23pub struct MonthIndex(pub f32);
24
25impl MonthIndex {
26    /// The default month value (`0.0` -> `MorningStar`).
27    pub const DEFAULT: Self = Self(0.0);
28
29    /// Creates a new `MonthIndex` instance with the specified value.
30    ///
31    /// # Example
32    /// ```
33    /// # use commonlibsse_ng::re::Calendar::MonthIndex;
34    /// let month = MonthIndex::new(5.0);
35    /// assert_eq!(month.0, 5.0);
36    /// ```
37    #[inline]
38    pub const fn new(value: f32) -> Self {
39        Self(value)
40    }
41
42    /// Returns the 1-based month (1..=12) if the value is valid, otherwise `None`.
43    ///
44    /// - `0.0` → `1` (January)
45    /// - `11.0` → `12` (December)
46    ///
47    /// Returns `None` if the value is out of the valid range (`0.0..=11.0`).
48    ///
49    /// # Example (Boundary Tests)
50    /// ```
51    /// # use commonlibsse_ng::re::Calendar::MonthIndex;
52    /// assert_eq!(MonthIndex::new(0.0).to_clamp_month(), Some(1));    // Morning Star
53    /// assert_eq!(MonthIndex::new(11.0).to_clamp_month(), Some(12));  // Evening Star
54    /// assert_eq!(MonthIndex::new(12.0).to_clamp_month(), None);      // Out of range
55    /// assert_eq!(MonthIndex::new(-1.0).to_clamp_month(), None);      // Out of range
56    /// ```
57    #[inline]
58    pub const fn to_clamp_month(self) -> Option<u32> {
59        let n = self.0;
60        match n {
61            0.0..12.0 => Some(n as u32 + 1),
62            _ => None,
63        }
64    }
65
66    /// Converts `MonthIndex` into `MonthInGame` enum if the value is valid.
67    ///
68    /// Returns `None` if the value is out of range (`0.0..=11.0`).
69    ///
70    /// # Example
71    /// ```
72    /// # use commonlibsse_ng::re::Calendar::{MonthIndex, MonthInGame};
73    /// let month = MonthIndex::new(0.0);
74    /// assert_eq!(month.to_enum(), Some(MonthInGame::MorningStar));
75    ///
76    /// let invalid_month = MonthIndex::new(12.0);
77    /// assert_eq!(invalid_month.to_enum(), None);
78    /// ```
79    #[inline]
80    pub const fn to_enum(self) -> Option<MonthInGame> {
81        MonthInGame::from_u32(self.0 as u32)
82    }
83}
84
85/// Represents the months of the year.
86///
87/// The internal values correspond to 0-based month indexing:
88/// - `0` → January (`MorningStar`)
89/// - `1` → February (`SunsDawn`)
90/// - `11` → December (`EveningStar`)
91///
92/// # Example
93/// ```
94/// # use commonlibsse_ng::re::Calendar::MonthInGame;
95/// let month = MonthInGame::FirstSeed;
96/// assert_eq!(month.as_str(), "First Seed");
97/// ```
98#[repr(u32)]
99#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub enum MonthInGame {
101    MorningStar = 0,
102    SunsDawn = 1,
103    FirstSeed = 2,
104    RainsHand = 3,
105    SecondSeed = 4,
106    Midyear = 5,
107    SunsHeight = 6,
108    #[default]
109    LastSeed = 7,
110    Hearthfire = 8,
111    Frostfall = 9,
112    SunsDusk = 10,
113    EveningStar = 11,
114    // Total, // unused
115}
116
117impl MonthInGame {
118    /// Returns the string representation of the month name.
119    ///
120    /// # Example
121    /// ```
122    /// # use commonlibsse_ng::re::Calendar::MonthInGame;
123    /// let month = MonthInGame::Midyear;
124    /// assert_eq!(month.as_str(), "Midyear");
125    /// ```
126    #[inline]
127    pub const fn as_str(&self) -> &'static str {
128        match *self {
129            Self::MorningStar => "Morning Star",
130            Self::SunsDawn => "Sun's Dawn",
131            Self::FirstSeed => "First Seed",
132            Self::RainsHand => "Rain's Hand",
133            Self::SecondSeed => "Second Seed",
134            Self::Midyear => "Midyear",
135            Self::SunsHeight => "Sun's Height",
136            Self::LastSeed => "Last Seed",
137            Self::Hearthfire => "Hearthfire",
138            Self::Frostfall => "Frostfall",
139            Self::SunsDusk => "Sun's Dusk",
140            Self::EveningStar => "Evening Star",
141        }
142    }
143
144    /// Converts a `u32` value into the corresponding `MonthInGame` enum.
145    ///
146    /// Returns `None` if the value is out of range (`0..=11`).
147    ///
148    /// # Example
149    /// ```
150    /// # use commonlibsse_ng::re::Calendar::MonthInGame;
151    ///
152    /// assert_eq!(MonthInGame::from_u32(0), Some(MonthInGame::MorningStar));  // January
153    /// assert_eq!(MonthInGame::from_u32(11), Some(MonthInGame::EveningStar)); // December
154    /// assert_eq!(MonthInGame::from_u32(12), None);                          // Out of range
155    /// assert_eq!(MonthInGame::from_u32(100), None);                         // Out of range
156    /// ```
157    #[inline]
158    pub const fn from_u32(month: u32) -> Option<Self> {
159        Some(match month {
160            0 => Self::MorningStar,
161            1 => Self::SunsDawn,
162            2 => Self::FirstSeed,
163            3 => Self::RainsHand,
164            4 => Self::SecondSeed,
165            5 => Self::Midyear,
166            6 => Self::SunsHeight,
167            7 => Self::LastSeed,
168            8 => Self::Hearthfire,
169            9 => Self::Frostfall,
170            10 => Self::SunsDusk,
171            11 => Self::EveningStar,
172            _ => return None,
173        })
174    }
175}
176
177impl From<MonthInGame> for MonthIndex {
178    #[inline]
179    fn from(month: MonthInGame) -> Self {
180        Self(month as u32 as f32)
181    }
182}
183
184impl TryFrom<MonthIndex> for MonthInGame {
185    type Error = &'static str;
186
187    #[inline]
188    fn try_from(index: MonthIndex) -> Result<Self, Self::Error> {
189        let u32_index = index.0 as u32;
190        Self::from_u32(u32_index).ok_or("Invalid month index")
191    }
192}
193
194impl core::fmt::Display for MonthInGame {
195    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196        write!(f, "{}", self.as_str())
197    }
198}